#ifndef SIMPLE_SUPPORT_ITERATOR_OUT_FLATTEN_TUPLE_HPP
#define SIMPLE_SUPPORT_ITERATOR_OUT_FLATTEN_TUPLE_HPP

#include <iterator> // std::iterator_traits std::output_iterator_tag
#include <type_traits> // std::enable_if_t
#include <utility> // std::forward
#include <tuple> // std::apply

#include "../type_traits/has_tuple_interface.hpp" // has_tuple_interface_v

namespace simple::support
{

	template <typename It>
	struct out_flatten_tuple
	{
		using value_type = typename std::iterator_traits<It>::value_type;
		using difference_type = typename std::iterator_traits<It>::difference_type;
		using pointer = typename std::iterator_traits<It>::pointer;
		using reference = typename std::iterator_traits<It>::reference;
		using iterator_category = std::output_iterator_tag;
		It out;

		out_flatten_tuple(It out)
			: out(out)
		{}

		out_flatten_tuple& operator++() { return *this; }
		out_flatten_tuple operator++(int) { out_flatten_tuple prev = *this; ++(*this); return prev; }

		class proxy
		{
			It& out;

			proxy(It& out) : out(out) {}
			friend struct out_flatten_tuple;
			public:
			proxy() = delete;
			proxy(const proxy&) = delete;
			proxy(const proxy&&) = delete;
			proxy& operator=(const proxy&) = delete;
			proxy& operator=(const proxy&&) = delete;

			template <typename T, std::enable_if_t<has_tuple_interface_v<T>>* = nullptr>
			const proxy& operator=(T&& x) const
			{
				std::apply([this](auto&&... x)
					{ ((*out = std::forward<decltype(x)>(x), ++out),...); },
					std::forward<T>(x));
				return *this;
			}

			// TODO: could theoretically convert underlying values to a tuple of references and be swappable
			// will need to fix the tuple size in advance though, so a bit less flexible in that way
			// all the operators ++/--/+/- would also work then, so can inherit the iterator category

		};

		const proxy operator*()
		{
			return {out};
		}
	};

} // namespace simple::support

#endif /* end of include guard */
